package com.course.service.impl;

import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.course.client.CourseUploadClient;
import com.course.config.FileServerConfigProperties;
import com.course.entity.dao.Course;
import com.course.entity.dto.CourseDto;
import com.course.entity.vo.CourseVo;
import com.course.entity.vo.UpLoadFileVo;
import com.course.exception.CourseException;
import com.course.executor.ServiceExecutor;
import com.course.filter.VisitDto;
import com.course.mapper.CourseMapper;
import com.course.service.*;
import com.course.util.FileSafeUploadUtil;
import com.easyCode.feature.mybaits.CustomPage;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;

import static com.course.constants.ServiceConstants.*;
import static com.course.entity.factory.CourseFactory.COURSE_CONVERTER;

/**
 * @author zdh
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course> implements ICourseService {
    @Autowired
    private IBigCourseTypeService iBigCourseTypeService;
    @Autowired
    private ISmallCourseTypeService iSmallCourseTypeService;
    @Autowired
    private CourseUploadClient courseUploadClient;
    @Autowired
    private FileServerConfigProperties fileServerConfigProperties;
    @Autowired
    private ICampusCourseService iCampusCourseService;
    @Autowired
    private ISubCampusCourseService iSubCampusCourseService;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    //----- 超级管理员才能做的事情

    /**
     * 检查课程大分类类型和小分类类型是否合法
     *
     * @return
     */
    @Override
    public String saveCourse(CourseVo courseVo) {
        Course.repeatNameCheck(courseVo.getCourseName(), this);
        courseVo.courseTypeCheck(iBigCourseTypeService, iSmallCourseTypeService);
        String taskId = UUID.randomUUID().toString();
        CourseVo.changeUploadState(redisTemplate,taskId,UPLOADING);
        CompletableFuture.runAsync(()->SpringUtil.getBean(CourseServiceImpl.class).upLoadCourseAsync(courseVo,taskId)
                ,SpringUtil.getBean("asyncExecutor", Executor.class));
        return taskId;
    }

    /**
     * 异步上传文件:
     * 1.利用redis生成唯一的任务序列号
     * 2.将任务序列号返回给前端
     * 3.前端不断轮询获取任务执行结果
     * 4.课程上传成功,将任务状态保存至redis,值为1
     * 5.课程上传失败,将任务状态保存至redis,值为0
     */
    @Transactional
    public void upLoadCourseAsync(CourseVo courseVo, String taskId) {
        Course course;
        List<Integer> uploadFileLogRecordList = new LinkedList<>();
        try {
            //1.上传文件,返回文件md5值--同时记录日志
            UpLoadFileVo upLoadFileVo = FileSafeUploadUtil.uploadZipFile(courseVo.getCourseZipFile(),
                    //解压目录为课程名
                    uploadFileLogRecordList, fileServerConfigProperties.getFileBucket(), courseVo.getCourseName());
            //3.执行本地事务
            course = COURSE_CONVERTER.toCourse(courseVo);
            course.fill(upLoadFileVo);
            save(course);
            //4.本地事务执行成功,删除日志
            FileSafeUploadUtil.deleteLogRecord(uploadFileLogRecordList);
            //5.设置状态为上传成功,保留30分钟
            CourseVo.changeUploadState(redisTemplate,taskId,UPLOAD_SUCCESS);
        } catch (Exception e) {
            //6.根据日志删除上传成功的文件
            log.error("课程发布出现异常: ", e);
            FileSafeUploadUtil.deleteFile(uploadFileLogRecordList);
            //7.设置状态为上传失败,保留30分钟
            CourseVo.changeUploadState(redisTemplate,taskId,UPLOAD_FAILURE);
            throw new CourseException("课程发布失败");
        }
    }

    /**
     * 删除课程文件,然后删除课程记录 --- 异步删除课程文件,存在删除失败可能
     */
    @Override
    public void delCourseById(Integer id) {
        Course course = getById(id);
        if (course == null) {
            return;
        }
        //检查当前课程是否还和主校区或者分校区相互关联
        iCampusCourseService.checkCourseReleationShip(id);
        iSubCampusCourseService.checkCourseReleationShip(id);
        removeById(id);
        CompletableFuture.runAsync(() -> {
            try {
                courseUploadClient.deleteFile(course.getCourseBucket(), course.getCourseDir());
            } catch (Exception e) {
                log.error("课程相关文件异步删除过程中出现异常: ", e);
            }
        }, ServiceExecutor.COURSE_THREAD_POOL);
    }

    /**
     * 上架或下架课程
     */
    @Override
    public void enableOrDisableCourse(Integer id, Integer enable) {
        updateById(Course.builder().id(id).enable(enable).build());
    }

    @Override
    public Map<String, String> checkCourseUpload(List<String> taskId) {
        return CourseVo.getUploadState(redisTemplate,taskId);
    }


    //------ 下面的请求都需要判断课程对应的用户是否拥有,或者查询课程时,只展示自己拥有的课程信息 

    @Override
    public CourseDto getCourseById(Integer id) {
        return VisitDto.
                handle(
                        //超级管理员访问
                        visitDto -> doGetCourseById(id, false),
                        visitDto -> {
                            //普通管理员访问
                            if (iCampusCourseService.managerHasCourse(id, visitDto.getCId())) {
                                return doGetCourseById(id, true);
                            }
                            return null;
                        }, visitDto -> {
                            //普通用户访问
                            if (iSubCampusCourseService.userHasCourse(id, visitDto.getCId())) {
                                return doGetCourseById(id, true);
                            }
                            return null;
                        });
    }

    private CourseDto doGetCourseById(Integer id, Boolean filterEnable) {
        Course course = getById(id);
        //课程的上架与下架只有超级管理员才有权管理
        if (filterEnable && course != null && course.getEnable() != 1) {
            return null;
        }
        return COURSE_CONVERTER.toCourseDto(course);
    }

    @Override
    public List<CourseDto> listCourse(CustomPage customPage) {
        return VisitDto.
                handle(
                        //超级管理员访问
                        visitDto -> {
                            List<Course> courseList = CustomPage.getResult(customPage, new Course(), this, null);
                            return COURSE_CONVERTER.toListCourseDto(courseList);
                        },
                        visitDto -> {
                            //普通管理员访问
                            List<Integer> idList = iCampusCourseService.getCourseIdListOfManager(customPage, visitDto.getCId());
                            if (ObjectUtils.isEmpty(idList)) {
                                return null;
                            }
                            return COURSE_CONVERTER.toListCourseDto(list(new QueryWrapper<Course>()
                                    .in("id", idList).eq("enable", 1)));
                        }, visitDto -> {
                            //普通用户访问
                            List<Integer> idList = iSubCampusCourseService.getCourseIdListOfUser(customPage, visitDto.getCId());
                            if (ObjectUtils.isEmpty(idList)) {
                                return null;
                            }
                            return COURSE_CONVERTER.toListCourseDto(list(new QueryWrapper<Course>()
                                    .in("id", idList).eq("enable", 1)));
                        });
    }
}
